/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2005
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
* 
* Author's email :    insane@dees.ufmg.br
* Author's website :  http://www.dees.ufmg.br/insane
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package br.ufmg.dees.insane.prep;

import java.util.List;

import br.ufmg.dees.insane.model.geo.GCurve;
import br.ufmg.dees.insane.model.geo.GPoint;
import br.ufmg.dees.insane.model.geo.GRegion;

public class BilinearToSerenT extends Bilinear{
	
	private boolean linearDivisor;
	
//*****************************************************************************
	
	public BilinearToSerenT(int ln, int le, int nppe, boolean ld, GRegion rg){
		this.lastNode = ln;
		this.lastElement = le;
		this.nPointsPerElement = nppe;
		this.linearDivisor = ld;
		this.region = rg;
		
		this.mapNodes();
		this.conectivite();
	}
	
//*****************************************************************************
	
	private void mapNodes(){
		int nPointsElementSide = nPointsPerElement/3 + 1;
		
		List curves = this.region.getCurves();
		GPoint[] curve1 = ((GCurve)curves.get(0)).getDiscrete((GPoint)region.getFirstPoint());
		GPoint[] curve2 = ((GCurve)curves.get(1)).getDiscrete(curve1[curve1.length-1]);
		GPoint[] curve3 = ((GCurve)curves.get(2)).getDiscrete(curve2[curve2.length-1]);
		GPoint[] curve4 = ((GCurve)curves.get(3)).getDiscrete(curve3[curve3.length-1]);
		
		int nPoints1 = curve1.length;
		int nPoints2 = curve2.length;
		
		double x, y;
		int ci, cj;
		for (int i = 0; i < nPoints2; i++) {
			ci = nPoints2-(i+1);
			double v = (double)i/(nPoints2-1);
			for (int j = 0; j < nPoints1; j++) {
				if ( i%(nPointsElementSide-1)==0 || j%(nPointsElementSide-1)==0 || j%(nPointsElementSide-1)==(i%(nPointsElementSide-1)) ) {
					cj = nPoints1-(j+1);
					lastNode++;
					double u = (double)j/(nPoints1-1);
					x =  (1.0-v)*curve1[ j].getX()+v*curve3[cj].getX()
						+(1.0-u)*curve4[ci].getX()+u*curve2[ i].getX()
						-(1.0-u)*(1.0-v)*curve1[0].getX()
						-u*(1.0-v)*curve1[nPoints1-1].getX()
						-(1.0-u)*v*curve3[nPoints1-1].getX()
						-u*v*curve3[0].getX();
					y =  (1.0-v)*curve1[ j].getY()+v*curve3[cj].getY()
						+(1.0-u)*curve4[ci].getY()+u*curve2[ i].getY()
						-(1.0-u)*(1.0-v)*curve1[0].getY()
						-u*(1.0-v)*curve1[nPoints1-1].getY()
						-(1.0-u)*v*curve3[nPoints1-1].getY()
						-u*v*curve3[0].getY();
					MapNode mn = new MapNode(x, y, lastNode);
					if ( i==0 || j==0 || i==(nPoints2-1) || j==(nPoints1-1) ) {
						mn.setBoundary( true );
					}
					this.nodes.add(mn);
				}
			}
		}
	}
	
//*****************************************************************************
	
	private void conectivite(){
		List curves = this.region.getCurves();
		GPoint[] curve1 = ((GCurve)curves.get(0)).getDiscrete((GPoint)region.getFirstPoint());
		GPoint[] curve2 = ((GCurve)curves.get(1)).getDiscrete(curve1[curve1.length-1]);
		GPoint[] curve3 = ((GCurve)curves.get(2)).getDiscrete(curve2[curve2.length-1]);
		GPoint[] curve4 = ((GCurve)curves.get(3)).getDiscrete(curve3[curve3.length-1]);
		int nPoints1 = curve1.length;
		int nPoints2 = curve2.length;
		int nPointsElementSide = nPointsPerElement/3 + 1;
		
		double xx, yy, xx1, yy1, xx2, yy2;
		int nElements1 = (nPoints1 - 1)/(nPointsElementSide - 1);
		int nElements2 = (nPoints2 - 1)/(nPointsElementSide - 1);
		int element = this.lastElement;
		MapNode[] corner = new MapNode[4];
		MapNode[] conec = new MapNode[3*(nPointsElementSide-1)];
		for (int i = 0; i < nElements2; i++) {
			for (int j = 0; j < nElements1; j++) {
				int no;
				corner[0] = (MapNode)nodes.get( i*(nPoints1+(2*nElements1+1)*(nPointsElementSide-2)) + j*(nPointsElementSide-1) + 1 - 1 );
				corner[1] = (MapNode)nodes.get( i*(nPoints1+(2*nElements1+1)*(nPointsElementSide-2)) + j*(nPointsElementSide-1) + nPointsElementSide - 1 );
				corner[2] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i+1)*(2*nElements1+1)*(nPointsElementSide-2) + j*(nPointsElementSide-1) + nPointsElementSide - 1 );
				corner[3] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i+1)*(2*nElements1+1)*(nPointsElementSide-2) + j*(nPointsElementSide-1) + 1 - 1 );
				double distance1 = corner[0].distance(corner[2]);
				double distance2 = corner[1].distance(corner[3]);
				//	algoritimo para gerar o contorno do elemento no sentido anti-horario
				if ( (distance1/distance2) < 1.01 ) {
					element++;
					no = -1;
					//	algoritimo para gerar elemento no sentido anti-horario
					// lado inferior
					for (int k = 1; k <= nPointsElementSide; k++) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i+1)*(2*nElements1+1)*(nPointsElementSide-2) + j*(nPointsElementSide-1) + k - 1 );
					}
					// diagonal "\"
					for (int k = (nPointsElementSide-3); k >= 0; k--) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i)*(2*nElements1+1)*(nPointsElementSide-2) + (2*(j+1)) + k*(2*nElements1+1) - 1 );
					}
					// vertice superior esquerdo
					no++;
					conec[no] = (MapNode)nodes.get( i*(nPoints1+(2*nElements1+1)*(nPointsElementSide-2)) + j*(nPointsElementSide-1) + 1 - 1 );
					xx1 = conec[no].getX();
					yy1 = conec[no].getY();
					// lado esquerdo
					for (int k = 1; k <= (nPointsElementSide-2); k++) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + i*(2*nElements1+1)*(nPointsElementSide-2) + (k-1)*(2*nElements1+1) + (2*j+1) - 1 );
					}
					
					MapElement me1 = new MapElement(element, conec, conec);
					me1.setType('T','S');
					this.elements.add(me1);
					
					element++;
					no = -1;
					// vertice inferior direito
					no++;
					conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i+1)*(2*nElements1+1)*(nPointsElementSide-2) + j*(nPointsElementSide-1) + nPointsElementSide - 1 );
					xx2 = conec[no].getX();
					yy2 = conec[no].getY();
					// lado direito
					for (int k = (nPointsElementSide-2); k >= 1; k--) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + i*(nPointsElementSide-2)*(2*nElements1+1) + (k-1)*(2*nElements1+1) + (2*j+3) - 1 );
					}
					// lado superior
					for (int k = nPointsElementSide; k > 0; k--) {
						no++;
						conec[no] = (MapNode)nodes.get( i*(nPoints1+(2*nElements1+1)*(nPointsElementSide-2)) + j*(nPointsElementSide-1) + k - 1 );
					}
					// diagonal "\"
					for (int k = 0; k <= (nPointsElementSide-3); k++) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i)*(2*nElements1+1)*(nPointsElementSide-2) + (2*(j+1)) + k*(2*nElements1+1) - 1 );
						
						if (linearDivisor) {
							double vv = (double)(k+1)/(nPointsElementSide-1);
							xx = (1.0-vv)*xx1+vv*xx2;
							yy = (1.0-vv)*yy1+vv*yy2;
							conec[no].setLocation(xx,yy);
						}
					}
					
					MapElement me2 = new MapElement(element, conec, conec);
					me2.setType('T','S');
					this.elements.add(me2);
				
				} else {
					element++;
					no = -1;
					//	algoritimo para gerar elemento no sentido anti-horario
					// lado superior
					for (int k = nPointsElementSide; k > 0; k--) {
						no++;
						conec[no] = (MapNode)nodes.get( i*(nPoints1+(2*nElements1+1)*(nPointsElementSide-2)) + j*(nPointsElementSide-1) + k - 1 );
					}
					// lado esquerdo
					for (int k = 1; k <= (nPointsElementSide-2); k++) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + i*(2*nElements1+1)*(nPointsElementSide-2) + (k-1)*(2*nElements1+1) + (2*j+1) - 1 );
					}
					// vertice inferior esquerdo
					no++;
					conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i+1)*(2*nElements1+1)*(nPointsElementSide-2) + j*(nPointsElementSide-1) + 1 - 1 );
					xx2 = conec[no].getX();
					yy2 = conec[no].getY();
					// diagonal "/"
					for (int k = (nPointsElementSide-3); k >= 0; k--) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i)*(2*nElements1+1)*(nPointsElementSide-2) + (2*(j+1)) + k*(2*nElements1+1) - 1 );
					}
					
					MapElement me1 = new MapElement(element, conec, conec);
					me1.setType('T','S');
					this.elements.add(me1);
					
					element++;
					no = -1;
					// lado inferior
					for (int k = 1; k <= nPointsElementSide; k++) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i+1)*(2*nElements1+1)*(nPointsElementSide-2) + j*(nPointsElementSide-1) + k - 1 );
					}
					// lado direito
					for (int k = (nPointsElementSide-2); k >= 1; k--) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + i*(nPointsElementSide-2)*(2*nElements1+1) + (k-1)*(2*nElements1+1) + (2*j+3) - 1 );
					}
					// vertice superior direito
					no++;
					conec[no] = (MapNode)nodes.get( i*(nPoints1+(2*nElements1+1)*(nPointsElementSide-2)) + j*(nPointsElementSide-1) + nPointsElementSide - 1 );
					xx1 = conec[no].getX();
					yy1 = conec[no].getY();
					// diagonal "/"
					for (int k = 0; k <= (nPointsElementSide-3); k++) {
						no++;
						conec[no] = (MapNode)nodes.get( (i+1)*(nPoints1) + (i)*(2*nElements1+1)*(nPointsElementSide-2) + (2*(j+1)) + k*(2*nElements1+1) - 1 );
						
						int ii = i*(nPointsElementSide-1)+k+1;
						int jj = j*(nPointsElementSide-1)+nPointsElementSide-1-(k+1);
						int ci = nPoints2-(ii+1);
						int cj = nPoints1-(jj+1);
						double uu = (double)jj/(nPoints1 - 1);
						double vv = (double)ii/(nPoints2 - 1);
						
						xx =	 (1.0-vv)*curve1[jj].getX()+vv*curve3[cj].getX()
								+(1.0-uu)*curve4[ci].getX()+uu*curve2[ii].getX()
								-(1.0-uu)*(1.0-vv)*curve1[0].getX()
								-(1.0-vv)*uu*curve1[nPoints1-1].getX()
								-(1.0-uu)*vv*curve3[nPoints1-1].getX()
								-uu*vv*curve3[0].getX();
						yy =	 (1.0-vv)*curve1[jj].getY()+vv*curve3[cj].getY()
								+(1.0-uu)*curve4[ci].getY()+uu*curve2[ii].getY()
								-(1.0-uu)*(1.0-vv)*curve1[0].getY()
								-(1.0-vv)*uu*curve1[nPoints1-1].getY()
								-(1.0-uu)*vv*curve3[nPoints1-1].getY()
								-uu*vv*curve3[0].getY();
								
						if (linearDivisor) {
							vv = (double)(k+1)/(nPointsElementSide-1);
							xx = (1.0-vv)*xx1+vv*xx2;
							yy = (1.0-vv)*yy1+vv*yy2;
						}
						conec[no].setLocation(xx,yy);
					}
					
					MapElement me2 = new MapElement(element, conec, conec);
					me2.setType('T','S');
					this.elements.add(me2);
					
				}
			}
		}
	}
	
//*****************************************************************************
}

